/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.boot.tasks;

import java.util.Set;
import java.util.Date;
import java.util.HashSet;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.internna.iwebmvc.dao.DAO;
import org.internna.iwebmvc.utils.Assert;
import org.internna.iwebmvc.model.Document;
import org.internna.iwebmvc.boot.StartupTask;
import org.internna.iwebjtracker.model.TrackerUser;
import org.internna.iwebmvc.model.security.UserImpl;
import org.internna.iwebjtracker.model.DefaultModule;
import org.internna.iwebjtracker.model.DefaultProject;
import org.internna.iwebjtracker.model.DefaultVersion;
import org.internna.iwebjtracker.utils.ConcurrencyUtils;
import org.internna.iwebjtracker.services.ProjectManager;
import org.internna.iwebjtracker.IWebJTrackerBootException;
import org.internna.iwebjtracker.services.TrackerUserManager;
import org.internna.iwebjtracker.model.DefaultConnectionDetails;
import org.internna.iwebjtracker.project.model.Project.ProjectType;
import org.internna.iwebjtracker.sonar.model.SonarConfiguration;
import org.springframework.util.FileCopyUtils;
import org.springframework.transaction.annotation.Transactional;

import static org.internna.iwebmvc.utils.StringUtils.hasText;

/**
 * Generates IWebMvc, IWebJTracker and Gmaps4JSF demo projects.
 *
 * @author Jose Noheda
 * @since 1.0
 */
public final class GenerateDemoProjectsStartupTask implements StartupTask {

    private static final Log logger = LogFactory.getLog(GenerateDemoProjectsStartupTask.class);

    private static final int http_port = 80;
    private static final String http = "http";

    private final DAO dao;
    private TrackerUserManager userManager;
    private boolean awaitTermination = true;
    private boolean generateDemoData = true;
    private String rootUser = "iwebjtracker";
    private final ProjectManager<DefaultConnectionDetails, DefaultModule, DefaultVersion, TrackerUser, DefaultProject> projectManager;

    /**
     * Builds an instance with the required data.
     *
     * @param dao any
     * @param projectManager any
     */
    public GenerateDemoProjectsStartupTask(DAO dao, ProjectManager<DefaultConnectionDetails, DefaultModule, DefaultVersion, TrackerUser, DefaultProject> projectManager) {
        this.dao = dao;
        this.projectManager = projectManager;
    }

    /**
     * Sets if the context must wait till this task is finished before continuing booting (useful because
     * it starts a number of threads). By default, true (required for unit testing results).
     *
     */
    public void setAwaitTermination(boolean awaitTermination) {
        this.awaitTermination = awaitTermination;
    }

    /**
     * If false no data will be generated (production ready instance). Defaults to true.
     *
     */
    public void setGenerateDemoData(boolean generateDemoData) {
        this.generateDemoData = generateDemoData;
    }

    /**
     * Change the default security user form "iwebjtracker".
     *
     * @param rootUser the admin username
     */
    public void setRootUser(String rootUser) {
        Assert.hasText(rootUser);
        this.rootUser = rootUser;
    }

    /**
     * Spring injection point.
     *
     * @param userManager a non null instance
     */
    public void setUserManager(TrackerUserManager userManager) {
        this.userManager = userManager;
    }

    /**
     * Generates data for demo purposes.
     */
    @Transactional
    @Override public void execute() {
        if (generateDemoData && (dao.first(DefaultProject.class) == null)) {
            TrackerUser root = userManager.getTrackerUser(rootUser);
            if (root != null) {
                DefaultConnectionDetails nemo = createNemo(root);
                Set<ProjectDataRetriever> projects = new HashSet<ProjectDataRetriever>(2);
                projects.add(new ProjectDataRetriever(createIWebMvc(root), nemo));
                projects.add(new ProjectDataRetriever(createGmaps4JSF(root), nemo));
                projects.add(new ProjectDataRetriever(createIWebJTracker(root), nemo));
                launch(projects);
            }
        }
    }

    private void launch(Set<ProjectDataRetriever> projects) {
        if (awaitTermination) {
            for (ProjectDataRetriever project : projects) {
                project.run();
            }
            if (logger.isDebugEnabled()) {
                logger.debug("All project data was retrieved successfully. Continue booting...");
            }
        } else {
            ConcurrencyUtils.parallelExecution(projects, awaitTermination);
        }
    }

    private DefaultConnectionDetails createNemo(TrackerUser root) {
        DefaultConnectionDetails nemoConnection = new DefaultConnectionDetails();
        nemoConnection.setPort(http_port);
        nemoConnection.setProtocol(http);
        nemoConnection.setPublicView(true);
        nemoConnection.setHost("nemo.sonarsource.org");
        nemoConnection.addOwner(root.getSecurityUser());
        nemoConnection.setDescription("Nemo - Sonar public instance");
        dao.create(nemoConnection);
        return nemoConnection;
    }

    private Document createLogo(DAO dao, UserImpl user, String name, String path) {
        Document logo = new Document();
        logo.setWidth(294);
        logo.setHeight(92);
        logo.setAuthor(user);
        logo.setTemporal(false);
        logo.setIdentifier(name + " logo");
        logo.setCreation(new Date());
        logo.setMimeType("image/png");
        logo.setLastModified(logo.getCreation());
        try {
            logo.setContents(FileCopyUtils.copyToByteArray(getClass().getResourceAsStream("/META-INF/images/demo/" + path + "-logo.png")));
        } catch (IOException ex) {
            throw new IWebJTrackerBootException(ex);
        }
        logo.setSizeInBytes(logo.getContents().length);
        dao.create(logo);
        return logo;
    }

    private DefaultProject createIWebMvc(TrackerUser root) {
        UserImpl user = root.getSecurityUser();
        DefaultProject iwebmvc = new DefaultProject();
        DefaultConnectionDetails iwebmvcConnection = new DefaultConnectionDetails();
        iwebmvcConnection.addOwner(user);
        iwebmvcConnection.setProtocol(http);
        iwebmvcConnection.setPort(http_port);
        iwebmvcConnection.setServerPath("/svn");
        iwebmvcConnection.setHost("internna.googlecode.com");
        iwebmvcConnection.setDescription("Anonymous connection to IWebMVc project");
        dao.create(iwebmvcConnection);
        iwebmvc.setPublicView(true);
        iwebmvc.setServerType("Subversion");
        iwebmvc.setProjectType(ProjectType.Maven);
        iwebmvc.setRelativePath("/trunk/IWebMvc2");
        iwebmvcConnection.addProject(iwebmvc);
        iwebmvc.setConnectionDetails(iwebmvcConnection);
        iwebmvc.addOwner(user);
        iwebmvc.addDeveloper(root);
        iwebmvc.setLogo(createLogo(dao, user, "IWebMvc", "iwebmvc"));
        return iwebmvc;
    }

    private DefaultProject createIWebJTracker(TrackerUser root) {
        UserImpl user = root.getSecurityUser();
        DefaultProject iwebjtracker = new DefaultProject();
        DefaultConnectionDetails iwebjtrackerConnection = new DefaultConnectionDetails();
        iwebjtrackerConnection.addOwner(user);
        iwebjtrackerConnection.setProtocol(http);
        iwebjtrackerConnection.setPort(http_port);
        iwebjtrackerConnection.setServerPath("/svn");
        iwebjtrackerConnection.setHost("iwebjtracker.googlecode.com");
        iwebjtrackerConnection.setDescription("Anonymous connection to IWebJTracker project");
        dao.create(iwebjtrackerConnection);
        iwebjtracker.setPublicView(true);
        iwebjtracker.setServerType("Subversion");
        iwebjtracker.setProjectType(ProjectType.Maven);
        iwebjtracker.setRelativePath("/trunk");
        iwebjtrackerConnection.addProject(iwebjtracker);
        iwebjtracker.setConnectionDetails(iwebjtrackerConnection);
        iwebjtracker.addOwner(user);
        iwebjtracker.addDeveloper(root);
        iwebjtracker.setLogo(createLogo(dao, user, "IWebJTracker", "iwebjtracker"));
        return iwebjtracker;
    }

    private DefaultProject createGmaps4JSF(TrackerUser root) {
        UserImpl user = root.getSecurityUser();
        DefaultProject gmaps = new DefaultProject();
        DefaultConnectionDetails gmapsConnection = new DefaultConnectionDetails();
        gmapsConnection.addOwner(user);
        gmapsConnection.setProtocol(http);
        gmapsConnection.setPort(http_port);
        gmapsConnection.setServerPath("/svn");
        gmapsConnection.setHost("gmaps4jsf.googlecode.com");
        gmapsConnection.setDescription("Anonymous connection to Gmaps4JSF project");
        dao.create(gmapsConnection);
        gmaps.setPublicView(true);
        gmaps.setServerType("Subversion");
        gmaps.setProjectType(ProjectType.Maven);
        gmaps.setRelativePath("/trunk");
        gmapsConnection.addProject(gmaps);
        gmaps.setConnectionDetails(gmapsConnection);
        gmaps.addOwner(user);
        gmaps.addDeveloper(root);
        gmaps.setLogo(createLogo(dao, user, "Gmaps4JSF", "gmaps4jsf"));
        return gmaps;
    }

    /**
     * Task name.
     *
     * @return "Generate data for IWebMvc/IWebJTracker projects"
     */
    @Override public String getTaskName() {
        return "Generate data for IWebMvc/IWebJTracker/GMaps4JSF projects";
    }

    /**
     * Sets the execution order to just after the framework tasks.
     *
     * @return FRAMEWORK_ORDER + 1
     */
    @Override public int order() {
        return APPLICATION_ORDER + 1;
    }

    private final class ProjectDataRetriever implements Runnable {

        private final DefaultProject project;
        private final DefaultConnectionDetails nemo;

        private ProjectDataRetriever(DefaultProject project, DefaultConnectionDetails nemo) {
            this.nemo = nemo;
            this.project = project;
        }

        @Override public void run() {
            String url = project.getSourceControlURL();
            if (logger.isDebugEnabled()) {
                logger.debug("Retrieving project data from URL [" + url + "]...");
            }
            String error = validateAndCreate();
            if (logger.isInfoEnabled()) {
                if (!hasText(error)) {
                    logger.info("Demo project [" + project.getProjectName() + "] was created successfully");
                } else {
                    logger.info("Demo project [" + project.getProjectName() + "] could not be created: " + error);
                }
            }
        }

        private String validateAndCreate() {
            String validation = null;
            try {
                validation = projectManager.validateProject(project);
                if (!hasText(validation)) {
                    dao.create(project);
                    createSonarConnectorConfiguration();
                }
            } catch (Exception ex) {
                validation = ex.getMessage();
            }
            return validation;
        }

        private void createSonarConnectorConfiguration() {
            SonarConfiguration configuration = new SonarConfiguration();
            configuration.setProject(project);
            configuration.setConnectionDetails(nemo);
            configuration.setResource(project.getMavenGroupId() + ":" + project.getMavenArtifactId());
            dao.create(configuration);
        }

    }

}
